package qgs.zisu.javaconcurrency.javaconcurrency.chapter10_LockTrick;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;

/**
 * @Author: 蔡翔
 * @Date: 2019/10/18 19:56
 * @Version 1.0
 *
 * （重点）好案例：讲到多线程 一个一个被塞到等待，并且一个接着一个被唤醒。
 */
public class BoolenLock implements LOCK {
    private boolean initValue;


    // initValue => true indicated the lock have been got
    // intiValue => false indicated the lock is free (can be get by other Thread)
    public BoolenLock(){
        initValue = false;
    }

    private Collection<Thread> blockedThreadCollection = new ArrayList<>();
    //记录当前锁，防止多线程环境下乱解锁。(这个成员变量比较重要)
    private Thread currentThread;


    @Override
    public synchronized void lock() throws InterruptedException,TimeOutException {
        // 对blockedThreadCollection 的操作都是上了锁的，都是线程安全的。
        while (initValue){
            // 来实现：一个线程去取锁，但是这个锁却被 其他线程取走了。
            blockedThreadCollection.add(Thread.currentThread());
            this.wait();
        }
        //即使刚开始blockedThreadCollection 没有数据remove 当前线程也不会报错。
        boolean remove = blockedThreadCollection.remove(Thread.currentThread());
        this.initValue = true;
        this.currentThread = Thread.currentThread();
    }

    // 在mills时间内 还没有被执行。就会包超时异常。
    @Override
    public synchronized void lock(long mills) throws InterruptedException, TimeOutException {
        if(mills<0){
            lock();
        }
        long hasRemaining = mills;
        long endTime = System.currentTimeMillis() + mills;
        while (initValue){
            if(hasRemaining <=0){
                throw new TimeOutException("Time out");
            }
            blockedThreadCollection.add(Thread.currentThread());
            //有wait 的方法，。这个方法必须加synchronized
            this.wait(mills);
            hasRemaining =endTime - System.currentTimeMillis();
            //Thread.sleep(10);
        }
        this.initValue = true;
        this.currentThread = Thread.currentThread();
    }

    @Override
    public synchronized void unlock() {
        //只有当前线程等于currentThread 的时候才允许解锁，否则解不了锁。
        if(currentThread == Thread.currentThread()){
            //把initValue 置为false 。。就代表 lock 会结束循环并且 执行后面的 remove操作
            this.initValue = false;
            Optional.of(Thread.currentThread()+"release the lock monitor")
                    .ifPresent(System.out::println);
            //唤醒 BoolenLock instance ，这个monitor的所有锁。
            this.notify();
        }
    }

    @Override
    public Collection<Thread> getBlockedThread() {
        //直接返回 会线程不安全，这样其他线程就可以对他进行修改了
        /*return this.blockedThreadCollection;*/
        //所以这里要返回一个不可修改的值。
        return Collections.unmodifiableCollection(blockedThreadCollection);
    }

    @Override
    public int getBlockedSize() {
        return blockedThreadCollection.size();
    }
}
